package res.input;

import java.io.File;
import java.io.IOException;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.logging.ConsoleHandler;
import java.util.logging.Level;
import java.util.logging.Logger;

import javax.swing.JOptionPane;

import org.jdom.Element;

import res.Pair;
import res.exceptions.InvalidInputGraphException;
import res.graph.Edge;
import res.input.enums.RoadSystem;

public class GridGraphReader extends DefaultXMLGraphReader {
	protected int gridwidth = 0;
	protected int gridheight = 0;

	public GridGraphReader(File filein) throws IOException, InvalidInputGraphException {
		super(filein);
	}

	public GridGraphReader(String filepath) throws InvalidInputGraphException,
			IOException {
		super(filepath);
	}
	
	@Override
	protected void calculateInitialSets(Edge edge) {
		int t = edge.getTarget().getOrd();
		Edge neigh = null;
		
		if(t % gridwidth != 0) { // Não Está na borda esquerda
			neigh = HSedges.get(new Pair<Integer, Integer>(t - 1, t));
			
			if(neigh != null) {
				if(neigh.getSource().getOrd() != edge.getSource().getOrd() || neigh.getTarget().getOrd() != edge.getTarget().getOrd()) {
					if(Collections.disjoint(edge.getSuccessors(), neigh.getSuccessors())) {
						neigh.addPartner(edge);
						edge.addPartner(neigh);
					} else {
						neigh.addCompetitor(edge);
						edge.addCompetitor(neigh);
					}
				}
			}
		}
		if((t+1) % gridwidth != 0) { // Não Está na borda direita
			neigh = HSedges.get(new Pair<Integer, Integer>(t + 1, t));
			
			if(neigh != null) {
				if(neigh.getSource().getOrd() != edge.getSource().getOrd() || neigh.getTarget().getOrd() != edge.getTarget().getOrd()) {
					if(Collections.disjoint(edge.getSuccessors(), neigh.getSuccessors())) {
						neigh.addPartner(edge);
						edge.addPartner(neigh);
					} else {
						neigh.addCompetitor(edge);
						edge.addCompetitor(neigh);
					}
				}
			}
		}
		if(t >= gridwidth) { // Não Está na borda superior
			neigh = HSedges.get(new Pair<Integer, Integer>(t - gridwidth, t));

			if(neigh != null) {
				if(neigh.getSource().getOrd() != edge.getSource().getOrd() || neigh.getTarget().getOrd() != edge.getTarget().getOrd()) {
					if(Collections.disjoint(edge.getSuccessors(), neigh.getSuccessors())) {
						neigh.addPartner(edge);
						edge.addPartner(neigh);
					} else {
						neigh.addCompetitor(edge);
						edge.addCompetitor(neigh);
					}
				}
			}
		}
		if((t+gridwidth) < numvertices) { // Não Está na borda inferior
			neigh = HSedges.get(new Pair<Integer, Integer>(t + gridwidth, t));
			
			if(neigh != null) {
				if(neigh.getSource().getOrd() != edge.getSource().getOrd() || neigh.getTarget().getOrd() != edge.getTarget().getOrd()) {
					if(Collections.disjoint(edge.getSuccessors(), neigh.getSuccessors())) {
						neigh.addPartner(edge);
						edge.addPartner(neigh);
					} else {
						neigh.addCompetitor(edge);
						edge.addCompetitor(neigh);
					}
				}
			}
		}
	}

	@Override
	protected void calculateSuccessors() {
		if(roadsystem != RoadSystem.NONE) {
			boolean nosucc = true;
			
			for(Edge e : HSedges.values()) {
				int from = e.getSource().getOrd();
				int to = e.getTarget().getOrd();
				int offset = to - from;
				int mult;
				
				if (roadsystem == RoadSystem.RIGHT)
					mult = 1;
				else
					mult = -1;
				
				nosucc = true;
				
				if(offset == 1) {
					if((to+1) % gridwidth != 0) {
						if(e.addSuccessor(HSedges.get(new Pair<Integer, Integer>(to, to + 1))))
							nosucc = false;
					}
					if((mult == 1 && to + gridwidth < numvertices) || (mult == -1 && to >= gridwidth)) {
						if(e.addSuccessor(HSedges.get(new Pair<Integer, Integer>(to, to + mult*gridwidth))))
							nosucc = false;
					}
				} else if(offset == -1) {
					if(to % gridwidth != 0) {
						if(e.addSuccessor(HSedges.get(new Pair<Integer, Integer>(to, to - 1))))
							nosucc = false;
					}
					if((mult == -1 && to + gridwidth < numvertices) || (mult == 1 && to >= gridwidth)) {
						if(e.addSuccessor(HSedges.get(new Pair<Integer, Integer>(to, to - mult*gridwidth))))
							nosucc = false;
					}
				} else if(offset == gridwidth) {
					if((mult == 1 && to % gridwidth != 0) || (mult == -1 && (to+1) % gridwidth != 0)) {
						if(e.addSuccessor(HSedges.get(new Pair<Integer, Integer>(to, to - mult*1))))
							nosucc = false;
					}
					if(to + gridwidth < numvertices) {
						if(e.addSuccessor(HSedges.get(new Pair<Integer, Integer>(to, to + gridwidth))))
							nosucc = false;
					}
				} else if(offset == -gridwidth) {
					if((mult == -1 && to % gridwidth != 0) || (mult == 1 && (to+1) % gridwidth != 0)) {
						if(e.addSuccessor(HSedges.get(new Pair<Integer, Integer>(to, to + mult*1))))
							nosucc = false;
					}
					if(to >= gridwidth) {
						if(e.addSuccessor(HSedges.get(new Pair<Integer, Integer>(to, to - gridwidth))))
								nosucc = false;
					}
				}
			}
		}
		
		super.calculateSuccessors();
	}
	
	@Override
	protected boolean isValidEdge(int from, int to) {
		int offset = from - to;
		if((offset != 1 || from % gridwidth == 0)
				&& (offset != -1 || to % gridwidth == 0)
				&& Math.abs(offset) != gridwidth) {
			Logger.getLogger(GridGraphReader.class.getName()).severe("A aresta de "+from+" para "+to+" não pode ocorrer num grid.");
			return false;
		} else
			return super.isValidEdge(from, to);
	}

	@Override
	protected void readOptions(List<Element> options) throws InvalidInputGraphException {
		if (options.isEmpty()) {
			Logger.getLogger(GridGraphReader.class.getName())
					.severe("Nenhuma opção foi informada. Ao menos a dimensão do grid deve ser informada.");
			throw new InvalidInputGraphException();
		}

		Iterator<Element> it = options.iterator(); // Necessário para poder usar
													// o remove()
		Element e = null;

		while (it.hasNext()) {
			e = it.next();

			String optname = e.getAttributeValue("name");

			if (optname != null) {
				String[] optvalues;

				if (optname.equalsIgnoreCase("graphtype")) {
					optvalues = e.getAttributeValue("values", "").split(
							optseparator);

					if (optvalues[0].equalsIgnoreCase("grid")
							&& optvalues.length >= 3) {
						try {
							gridwidth = Integer.parseInt(optvalues[1]);
							gridheight = Integer.parseInt(optvalues[2]);
						} catch (NumberFormatException ex) {
							Logger.getLogger(GridGraphReader.class.getName())
									.severe("Esperava-se um parâmetro inteiro dentre os valores da opção ["
											+ optname
											+ "] configurada para o valor ["
											+ optvalues[0] + "].");
						} finally {
							it.remove();
						}

						if (optvalues.length > 3)
							Logger.getLogger(GridGraphReader.class.getName())
									.info("Um número maior de valores do que o necessário foi informado para a opção ["
											+ optname
											+ "] configurada para o valor ["
											+ optvalues[0]
											+ "]. Apenas 3 valores eram necessários.");
					}
				}
			}
		}

		super.readOptions(options);

		if (gridheight <= 0 || gridwidth <= 0)
			Logger.getLogger(GridGraphReader.class.getName())
					.severe("Os valores de largura ou altura do grid devem ser estritamente positivos.");
		else if (gridheight * gridwidth != numvertices)
			Logger.getLogger(GridGraphReader.class.getName())
					.severe("Os valores de largura ou altura do grid são inválidos. Os valores informados não são compatíveis com o número de vértices do grafo.");
		else
			return;

		throw new InvalidInputGraphException();
	}

	public int getGridWidth() {
		return gridwidth;
	}

	public int getGridHeight() {
		return gridheight;
	}

}
